package com.idempotent.xgfidempotent.config.aop;

import java.io.IOException;

import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.common.util.concurrent.RateLimiter;

import lombok.extern.slf4j.Slf4j;

import com.idempotent.xgfidempotent.config.ext.ExtRateLimiter;
import com.idempotent.xgfidempotent.factory.ExtApiIdempotentFactory;
import com.idempotent.xgfidempotent.ratelimiter.RateLimiterInterface;
import com.xgf.common.xgfcommon.result.R;
@Aspect
@Component
@Slf4j
@ConfigurationProperties
@Order(value = 100)
public class ExtRateLimiterAop {
	
	@Value("${xgf.idempotent.derail}")
	private boolean onOff;

	// 存放接口是否已经存在
	private static ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>();

	@Pointcut("@annotation(com.idempotent.xgfidempotent.config.ext.ExtRateLimiter)")
	public void extRateLimiterAop() {
	}
	
	@Around("extRateLimiterAop()")
	public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
		// 使用Java反射技术获取方法上是否有@ExtRateLimiter注解类
		ExtRateLimiter extRateLimiter = signature.getMethod().getDeclaredAnnotation(ExtRateLimiter.class);
		if (extRateLimiter == null) {
			// 正常执行方法
			Object proceed = proceedingJoinPoint.proceed();
			return proceed;
		}
		// ############获取注解上的参数 配置固定速率 ###############
		// 获取配置的速率
		double value = extRateLimiter.value();
		// 获取等待令牌等待时间
		long timeOut = extRateLimiter.timeOut();
		RateLimiter rateLimiter = getRateLimiter(value, timeOut);
		// 判断令牌桶获取token 是否超时
		boolean tryAcquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
		if (!tryAcquire) {
			return serviceDowng(extRateLimiter);
		}
		// 获取到令牌,直接执行..
		Object proceed = proceedingJoinPoint.proceed();
		return proceed;

	}

	// 获取RateLimiter对象
	private RateLimiter getRateLimiter(double value, long timeOut) {
		// 获取当前URL
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		String requestURI = request.getRequestURI();
		RateLimiter rateLimiter = null;
		if (!rateLimiterMap.containsKey(requestURI)) {
			// 开启令牌桶限流
			rateLimiter = RateLimiter.create(value); // 独立线程
			rateLimiterMap.put(requestURI, rateLimiter);
		} else {
			rateLimiter = rateLimiterMap.get(requestURI);
		}
		return rateLimiter;
	}

	// 服务降级
	private Object serviceDowng(ExtRateLimiter extRateLimiter) throws IOException {
		// 执行服务降级处理
		log.error("执行降级方法,亲,服务器忙！请稍后重试!");
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletResponse response = attributes.getResponse();
		response.setHeader("Content-type", "text/html;charset=UTF-8");
		R<Object> downgrade = R.downgrade();
		try {
		//说明指定了 自己的降级方法
		if(extRateLimiter.extRateLimiter() != null) {
			RateLimiterInterface newInstance = (RateLimiterInterface)extRateLimiter.extRateLimiter().newInstance();
			Object result = newInstance.downgrade();
			return result;
		}
		
			
			return downgrade;
		} catch (Exception e) {
			log.error(e.toString());
		} finally {
			
		}
		return response;

	}


}
